package com.croqodile {
    import flash.display.MovieClip;
    import flash.system.Security;
    import com.senocular.utils.Output;
    import flash.utils.Timer;
    import flash.events.*;
    import com.croqodile.*;
    import com.croqodile.events.*;
    
    public class SnapshottingController extends Controller {
	
	protected static const SNAPSHOT_SERVER_HOST:String = "localhost";
	protected static const SNAPSHOT_SERVER_PORT:int = 5002;
	
	protected var _routerSnapshotCon:RouterSnapshotConnection;
	
	public static function create(islandClass:Class, args:Array,config:Object):Controller{

	    config.policyHost = (config.policyHost ? config.policyHost : POLICY_SERVER_HOST);
	    config.policyPort = (config.policyPort ? config.policyPort : POLICY_SERVER_PORT);

	    config.routerHost = (config.routerHost ? config.routerHost : DEFAULT_ROUTER_HOST);
	    config.routerPort = (config.routerPort ? config.routerPort : DEFAULT_ROUTER_PORT);

	    config.snapshotHost = (config.snapshotHost ? config.snapshotHost : SNAPSHOT_SERVER_HOST);
	    config.snapshotPort = (config.snapshotPort ? config.snapshotPort : SNAPSHOT_SERVER_PORT);

	    Security.loadPolicyFile("xmlsocket://" + config.policyHost + ":" + config.policyPort);

	    var newCont:SnapshottingController = new SnapshottingController();
	    newCont._userId = newCont.genUserId();
	    newCont._island = islandClass.create.apply(islandClass, args);
	    newCont._island.setController(newCont);
	    
	    //Note! Messages will begin flowing immediately!
	    newCont._routerCon = RouterMessageConnection.create(newCont._userId,
								config.routerHost,
								config.routerPort);
	    newCont._routerCon.addEventListener(RouterConnection.CONNECTION_CLOSED,
						newCont.onRouterConnectionClosed);
	    //This will be replaced once the snapshot is installed.
	    newCont._routerCon.addEventListener(ExternalMessageEvent.type,
						newCont.onMessageFromRouterBuffered);
	    
	    
	    //Connection to the snapshot server makes implicit snapshot request!
	    newCont._routerSnapshotCon = RouterSnapshotConnection.create(newCont._userId,
									 config.snapshotHost,
									 config.snapshotPort);
	    newCont._routerSnapshotCon.addEventListener(SnapshotReceivedEvent.type,
							newCont.onSnapshotReceived);
	    newCont._routerSnapshotCon.addEventListener(RouterSnapshotConnection.NO_SNAPSHOT_AVAILABLE,
							newCont.onNoSnapshotAvailable);
	    newCont._routerSnapshotCon.addEventListener(RouterSnapshotConnection.NO_SNAPSHOT_NECESSARY,
							newCont.onNoSnapshotNecessary);
	    newCont._routerSnapshotCon.addEventListener(RouterSnapshotConnection.SNAPSHOT_REQUESTED,
							newCont.onSnapshotRequested);
	    
	    return newCont;
	}
	
	///////////////////////////
        //    Event Handlers	 //
        ///////////////////////////
	
	protected function onSnapshotRequested(event:Event):void {
	    this._routerSnapshotCon.sendSentence(this._island.snapshot());
	}
	
	protected function onSnapshotReceived(event:SnapshotReceivedEvent):void {
	    this._island.initFromSnapshot(event.snapshot);
	    
	    var time:Number = this._island.time();
	    for each(var m:ExternalMessage in this._msgBuffer){
		    
		    //Timestamps are guaranteed by router to be 
		    //always increasing, never repeating
		    if(m.executionTime() > time){
			this._island.advanceToExternalMessage(m);
		    }
		}
	    this._msgBuffer = [];
	    this.onSnapshotResolved();
	}
	
	protected function onNoSnapshotAvailable(event:Event):void {
	    throw new Error("Could not get a snapshot :(");
	}

	protected function onNoSnapshotNecessary(event:Event):void {
	    for each(var m:ExternalMessage in this._msgBuffer){
		    this._island.advanceToExternalMessage(m);
		}
	    Output.trace("Executed " + this._msgBuffer.length + " buffered messages");
	    this._msgBuffer = [];
	    this.onSnapshotResolved();
	}
	
	protected function onSnapshotResolved():void {
	    this._routerCon.removeEventListener(ExternalMessageEvent.type,
						this.onMessageFromRouterBuffered);
	    this._routerCon.addEventListener(ExternalMessageEvent.type,
					     this.onMessageFromRouter);
	    
	    //Start waiting for the main message connection..
	    this._routerCon.addEventListener(RouterConnection.CONNECTION_READY,
					     this.onRouterConnectionReady);
	    
	    //..might have already connected while we were grabbing the snapshot, so..
	    if(this._routerCon.ready()){
		this.dispatchEvent(new RouterConnectionReadyEvent());
	    }
	}
	
	//Buffer the messages while the snapshot is loading..
	protected function onMessageFromRouterBuffered(e:ExternalMessageEvent):void {
	    this._msgBuffer.push(ExternalMessage.fromRouterString(e.msg, this._island));
	}
	
    }
}


